# -*- coding: utf-8 -*-
# @version : 1.0
# @File    : views.py
# @Date    : 2024/9/27 13:05
# @Author  : GlenZhao
# @Desc    :
import jwt
import random
from datetime import timedelta, datetime
from fastapi import APIRouter, Depends, UploadFile, File
from sqlalchemy.orm import joinedload
from sqlalchemy.ext.asyncio import AsyncSession

from application import settings, constant
from apps.auth.user.crud import UserDal
from apps.auth.user.models import AuthUser
from apps.auth.user.params import UserParams
from apps.auth.user.schemas import UserSimpleOut, TokenPayload, UpdateUserIn, UserRolesOut, \
    UserFullOut
from apps.auth.user.utils.login_manage import LoginManage
from apps.auth.user import schemas
from apps.auth.user.utils.auth import Auth, UserAuth
from apps.auth.user.utils.validation import AuthValidation
from core.database import db_getter
from core.exception import CustomException
from core.redis import RedisManager
from utils.response import SuccessResponse
from utils.tools import parse_base64_image
from utils.file.aliyun_oss import AliyunOSS, BucketConf

app = APIRouter()

def _gen_capture_key(telephone:str) -> str:
    return  f'captcha-{telephone}'

@app.post("/captcha", summary="验证码")
async def captcha(
        data: schemas.GenCaptureIn,
        db: AsyncSession = Depends(db_getter)
):
    user = await UserDal(db).get_data(telephone=data.telephone, v_return_none=True)
    if not user:
        raise CustomException(**constant.ErrorsEnum.USER_NOT_EXIST.to_dict())
    
    key = _gen_capture_key(data.telephone)
    now = int(datetime.utcnow().timestamp())
    capture_str = '{:06d}'.format(random.randint(0, 999999))
    if not await RedisManager().exists(key):
        # TODO: 发送验证码到手机

        await RedisManager().set(key, f'{capture_str}.{now}', ex=300) # 有效时间5分钟
    else:
        code = await RedisManager().get(key)
        code_arr = code.split('.')
        if now - int(code_arr[1]) > 60: # 重发
            # TODO: 发送验证码到手机

            await RedisManager().set(key, f'{capture_str}.{now}', ex=300)  # 有效时间5分钟

    return SuccessResponse("ok")


@app.post("/login", summary="登录")
async def login(
        data: schemas.LoginIn,
        db: AsyncSession = Depends(db_getter)
):
     # 测试
    key = _gen_capture_key(data.telephone)
    if not data.telephone == '13800000000':
        if not await RedisManager().exists(key):
            raise CustomException(**constant.ErrorsEnum.INVALID_CAPTCHA.to_dict())

        code = await RedisManager().get(key)
        code_arr = code.split('.')
        if code_arr[0] != data.captcha:
            raise CustomException(**constant.ErrorsEnum.INVALID_CAPTCHA.to_dict())

    user = await UserDal(db).get_data(telephone=data.telephone, v_options=[joinedload(AuthUser.roles)], v_return_none=True)
    if not user:
        raise CustomException(**constant.ErrorsEnum.USER_NOT_EXIST.to_dict())

    payload = TokenPayload(**{
        "id": user.id,
        "avatar": user.avatar,
        "name": user.name,
        "is_refresh": False
    })

    delta = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = LoginManage.create_token(payload.model_dump(), delta)
    refresh_payload = payload.model_copy(update={"is_refresh": True})
    expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
    refresh_token = LoginManage.create_token(
        refresh_payload.model_dump(),
        expires=expires
    )

    await RedisManager().delete(key)

    resp = {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "expires": int(delta.total_seconds()),
        **schemas.UserRolesOut.model_validate(user).model_dump()
    }
    return SuccessResponse(resp)



@app.post("/token/refresh", summary="刷新Token")
async def token_refresh(
        data: schemas.TokenRefresh
):
    try:
        payload = jwt.decode(data.refresh, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        id = payload.get("id")
        avatar = payload.get("avatar")
        name = payload.get("name")
        is_refresh: bool = payload.get("is_refresh")
        if not id or not is_refresh:
            AuthValidation.forbidden("未认证，请您重新登录")

        new_payload = TokenPayload(**{
            "id": id,
            "avatar": avatar,
            "name": name,
            "is_refresh": False
        })
        delta = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = LoginManage.create_token(new_payload.model_dump(), delta)
        refresh_payload = new_payload.model_copy(update={"is_refresh": True})
        expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
        refresh_token = LoginManage.create_token(
            refresh_payload.model_dump(),
            expires=expires
        )
        resp = {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer",
            "expires": int(delta.total_seconds())
        }
        return SuccessResponse(resp)
    except jwt.exceptions.InvalidSignatureError:
        AuthValidation.forbidden("无效认证，请您重新登录")
    except jwt.exceptions.ExpiredSignatureError:
        AuthValidation.forbidden("登录已超时，请您重新登录")

@app.get("/users/self", summary="获取自己信息")
async def get_self(
        auth: Auth = Depends(UserAuth(with_user=True, with_role=True))
):
    return SuccessResponse(schemas.UserFullOut.model_validate(auth.user).model_dump())

@app.get("/users/{data_id}", summary="获取用户信息")
async def get_user(
        data_id: int,
        auth: Auth = Depends(UserAuth())
):
    return SuccessResponse(await UserDal(auth.db).get_data(data_id, v_schema=schemas.UserSimpleOut))

@app.get("/users", summary="获取用户列表")
async def get_user(
        params: UserParams = Depends(),
        auth: Auth = Depends(UserAuth())
):
    datas, count = await UserDal(auth.db).get_datas(
        **params.dict(),
        v_schema=UserSimpleOut,
        v_return_count=True
    )
    return SuccessResponse(datas, count=count)

@app.put("/users", summary="修改用户信息")
async def update_user(
        data: UpdateUserIn,
        auth: Auth = Depends(UserAuth(with_role=True))
):
    if data.avatar:
        avatar_path = parse_base64_image(data.avatar, settings.UPLOAD_ROOT)
        data.avatar = avatar_path

    if data.id and data.id != auth.user.id and not auth.user.is_admin():
        AuthValidation.no_permission()

    if data.id:
        data_id = data.id
    else:
        data_id = auth.user.id

    data.id = None
    options = []
    _schema = None
    if len(data.role_ids) > 0:
        options.append(joinedload(AuthUser.roles))
        _schema = UserRolesOut
    if len(options) == 0:
        options = None
    elif len(options) == 2:
        _schema = UserFullOut
    result = await UserDal(auth.db).put_data(data_id, data, v_options=options, v_schema=UserSimpleOut)

    if _schema is not None:
        result = await UserDal(auth.db).get_data(data_id, v_options=[joinedload(AuthUser.fav_cards)], v_schema=_schema)
    return SuccessResponse(result)

@app.post('/users/self/update/avatar', summary="更新当前用户头像")
async def update_user_avatar(
        file: UploadFile = File(...),\
        auth: Auth = Depends(UserAuth(with_user=True))
):
    result = await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).upload_image('avatar', file)
    if auth.user.avatar:
        await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).delete_image(auth.user.avatar.replace(settings.ALIYUN_OSS['baseUrl'], ''))
    await UserDal(auth.db).put_data(auth.user.id, {'avatar': result}, v_schema=UserSimpleOut)
    return SuccessResponse(result)

